﻿using System;
using OverStore.Configuration.Validation;
using OverStore.Exceptions.Configuration;
using OverStore.Runtime.OverStoreContext;
using OverStore.Runtime.ObjectRepository.ChangesTracker;
using System.Collections.Generic;
using OverStore.Runtime.ObjectRepository.Attacher;
using System.Linq;
using OverStore.Runtime.ObjectRepository;
using OverStore.Runtime.ObjectRepository.Depot;
using OverStore.Runtime.ObjectRepository.CommandSet;
using OverStore.Runtime.ObjectRepository.Mapping;
using OverStore.Runtime.ObjectRepository.ErrorTransformer;
using OverStore.Runtime.ObjectRepository.Reader;

namespace OverStore.Configuration.ObjectRepository
{

    /// <summary>
    /// Contains configuration parameters allowes to create instances of <see cref="IRepository{TInstance}"/>,
    /// which uses for store instances of single type.
    /// </summary>
    /// <typeparam name="TInstance">The type of the instance.</typeparam>
    /// <typeparam name="TIdentifier">The type of the instance identifier.</typeparam>
    public sealed class PersistenceObjectRepositoryConfiguration<TInstance, TIdentifier> : RepositoryConfigurationBase<TInstance>, ISupportValidation
        where TInstance : class
    {
        /// <summary>
        /// Get or set object depot
        /// </summary>
        public IComponentConfiguration<IDepot<TInstance, TIdentifier>> Depot { get; set; }

        /// <summary>
        /// Gets or sets the configuration of changes tracker.
        /// </summary>
        public IComponentConfiguration<IChangesTracker<TInstance>> ChangesTracker { get; set; }

        /// <summary>
        /// Gets or sets the command set configuration.
        /// </summary>
        public IComponentConfiguration<ICommandSet> Commands { get; set; }

        /// <summary>
        /// Gets or sets the mapper configuration.
        /// </summary>
        public IComponentConfiguration<IMapper<TInstance>> Mapper { get; set; }

        /// <summary>
        /// Gets or sets the configuration of the attacher.
        /// </summary>
        public IComponentConfiguration<IAttacher<TInstance, TIdentifier>> Attacher { get; set; }

        /// <summary>
        /// Gets or sets the error transformer.
        /// </summary>
        public IComponentConfiguration<IErrorTransformer<TInstance>> ErrorTransformer { get; set; }

        /// <summary>
        /// Gets or sets the parameterless reader.
        /// </summary>
        public IComponentConfiguration<IReader<TInstance, TIdentifier>> ParameterlessReader { get; set; }

        /// <summary>
        /// Gets or sets the collection of parameterized readers.
        /// </summary>
        /// <value>The parameterized readers.</value>
        public IEnumerable<IComponentConfiguration<IReader<TInstance, TIdentifier>>> ParameterizedReaders { get; set; }

        /// <summary>
        /// Creates the new instance of <see cref="IRepository{TInstance}"/>.
        /// </summary>
        /// <param name="context">The current context provides set of services may be used by <see cref="IRepository{TInstance}"/> instance.</param>
        /// <returns>
        /// The new instance of <see cref="IRepository{TInstance}"/> type.
        /// </returns>
        public override IRepository<TInstance> CreateComponent(IOverStoreContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (Depot == null)
                throw this.ConfigurationException("Depot is missing.");
            if (Commands == null)
                throw this.ConfigurationException("Commands are missing.");
            if (Mapper == null)
                throw this.ConfigurationException("Mapper is missing.");

            return new PersistenceObjectRepository<TInstance, TIdentifier>(
                            context.PersistenceServiceProvider,
                            CallbackManager == null ? null : CallbackManager.CreateComponent(context),
                            Depot.CreateComponent(context),
                            DeferredLoader == null ? null : DeferredLoader.CreateComponent(context),
                            ChangesTracker == null ? null : ChangesTracker.CreateComponent(context),
                            Commands.CreateComponent(context),
                            context.DataStorage,
                            Mapper.CreateComponent(context),
                            ErrorTransformer == null ? null : ErrorTransformer.CreateComponent(context),
                            Attacher == null ? null : Attacher.CreateComponent(context),
                            context.ScopeManager,
                            ParameterlessReader == null ? null : ParameterlessReader.CreateComponent(context),
                            ParameterizedReaders == null ? null : ParameterizedReaders.Select(c => c.CreateComponent(context)));
        }

        /// <summary>
        /// Perform component validation. Method should validate only
        /// component configuration itself, not sub components.
        /// If configuration is valid, null result should returns.
        /// </summary>
        /// <returns>
        ///     <see cref="T:OverStore.Configuration.Validation.ValidationResults"/> describes errors found in component configuration.
        /// </returns>
        ValidationResult ISupportValidation.Validate()
        {
            return Validate()
                    .AddError(Depot == null, "Depot is null")
                    .AddError(Commands == null, "Command Set is missing.")
                    .AddError(Mapper == null, "Mapper is missing.")
                    .ValidateToNestedResult(Depot)
                    .ValidateToNestedResult(ChangesTracker)
                    .ValidateToNestedResult(Commands)
                    .ValidateToNestedResult(Mapper);
        }
    }
}